{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_intro_md"
      },
      "source": [
        "# Ordering"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_desc_md"
      },
      "source": [
        "An `Ordering` specifies the order in which variables are eliminated during inference (e.g., Gaussian elimination, multifrontal QR). The choice of ordering significantly impacts the computational cost and fill-in (sparsity) of the resulting Bayes net or Bayes tree.\n",
        "\n",
        "GTSAM provides several algorithms to compute good orderings automatically, such as COLAMD and METIS, or allows you to specify a custom ordering."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_colab_md"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Ordering.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ordering_pip_code",
        "tags": [
          "remove-cell"
        ]
      },
      "outputs": [],
      "source": [
        "%pip install --quiet gtsam-develop"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "id": "ordering_import_code"
      },
      "outputs": [],
      "source": [
        "import gtsam\n",
        "from gtsam import Ordering\n",
        "# Need graph types\n",
        "from gtsam import SymbolicFactorGraph\n",
        "from gtsam import symbol_shorthand\n",
        "import graphviz\n",
        "\n",
        "X = symbol_shorthand.X\n",
        "L = symbol_shorthand.L"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_create_md"
      },
      "source": [
        "## Creating an Ordering\n",
        "\n",
        "Orderings can be created manually or computed automatically from a factor graph."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ordering_manual_code",
        "outputId": "6789abcd-ef01-2345-6789-abcdef012345"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Manual Ordering: Position 0: x1, l1, x2, l2, x0\n"
          ]
        }
      ],
      "source": [
        "# Manual creation (list of keys)\n",
        "manual_ordering = Ordering([X(1), L(1), X(2), L(2), X(0)])\n",
        "manual_ordering.print(\"Manual Ordering: \")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "COLAMD Ordering: Position 0: l1, x0, x1, l2, x2\n"
          ]
        }
      ],
      "source": [
        "# Automatic creation requires a factor graph\n",
        "# Let's use a simple SymbolicFactorGraph for structure\n",
        "graph = SymbolicFactorGraph()\n",
        "graph.push_factor(X(0))\n",
        "graph.push_factor(X(0), X(1))\n",
        "graph.push_factor(X(1), X(2))\n",
        "graph.push_factor(X(0), L(1))\n",
        "graph.push_factor(X(1), L(1))\n",
        "graph.push_factor(X(1), L(2))\n",
        "graph.push_factor(X(2), L(2))\n",
        "\n",
        "# COLAMD ordering\n",
        "colamd_ordering = Ordering.ColamdSymbolicFactorGraph(graph)\n",
        "colamd_ordering.print(\"COLAMD Ordering: \")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_auto_intro_md"
      },
      "source": [
        "## Automatic Ordering Algorithms: COLAMD vs METIS\n",
        "\n",
        "GTSAM provides algorithms to automatically compute an elimination ordering from a factor graph. Two common algorithms are:\n",
        "\n",
        "1.  **COLAMD (Column Approximate Minimum Degree):** A greedy algorithm that aims to minimize *fill-in* at each elimination step. It typically produces orderings that are good for sparse direct methods executed sequentially.\n",
        "2.  **METIS:** A graph partitioning algorithm. It aims to find orderings that partition the graph well, often leading to more balanced elimination trees which can be beneficial for parallel computation and sometimes reduce overall fill-in compared to purely local greedy methods like COLAMD, especially on large, structured problems.\n",
        "\n",
        "Let's illustrate the difference using a 2D grid factor graph."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "id": "ordering_grid_setup_code"
      },
      "outputs": [],
      "source": [
        "# Create a 3x4 grid graph\n",
        "ROWS, COLS = 3, 4\n",
        "\n",
        "# Use 'x' symbols for grid nodes\n",
        "X_grid = lambda r, c: X(10 * (r + 1) + c + 1)\n",
        "\n",
        "\n",
        "def create_grid_graph():\n",
        "    \"\"\"Creates a SymbolicFactorGraph representing a 2D grid.\"\"\"\n",
        "    graph = SymbolicFactorGraph()\n",
        "    keys = []\n",
        "    positions = {}\n",
        "    for r in range(ROWS):\n",
        "        for c in range(COLS):\n",
        "            key = X_grid(r, c)\n",
        "            positions[key] = gtsam.Point2(c, COLS-r)\n",
        "            keys.append(key)\n",
        "            # Add binary factors connecting to right and down neighbors\n",
        "            if c + 1 < COLS:\n",
        "                key_right = X_grid(r, c + 1)\n",
        "                graph.push_factor(key, key_right)\n",
        "            if r + 1 < ROWS:\n",
        "                key_down = X_grid(r + 1, c)\n",
        "                graph.push_factor(key, key_down)\n",
        "    return graph, keys, positions\n",
        "\n",
        "\n",
        "grid_graph, grid_keys, positions = create_grid_graph()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_grid_viz_md"
      },
      "source": [
        "Here's the structure of our grid graph. Edges represent factors connecting variables (nodes)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/svg+xml": [
              "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
              "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
              " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
              "<!-- Generated by graphviz version 2.50.0 (0)\n",
              " -->\n",
              "<!-- Pages: 1 -->\n",
              "<svg width=\"278pt\" height=\"188pt\"\n",
              " viewBox=\"0.00 0.00 278.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
              "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n",
              "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 274,-184 274,4 -4,4\"/>\n",
              "<!-- var8646911284551352331 -->\n",
              "<g id=\"node1\" class=\"node\">\n",
              "<title>var8646911284551352331</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"27\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x11</text>\n",
              "</g>\n",
              "<!-- var8646911284551352332 -->\n",
              "<g id=\"node2\" class=\"node\">\n",
              "<title>var8646911284551352332</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"99\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x12</text>\n",
              "</g>\n",
              "<!-- var8646911284551352331&#45;&#45;var8646911284551352332 -->\n",
              "<g id=\"edge1\" class=\"edge\">\n",
              "<title>var8646911284551352331&#45;&#45;var8646911284551352332</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M54.22,-162C59.95,-162 66.01,-162 71.74,-162\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352341 -->\n",
              "<g id=\"node5\" class=\"node\">\n",
              "<title>var8646911284551352341</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x21</text>\n",
              "</g>\n",
              "<!-- var8646911284551352331&#45;&#45;var8646911284551352341 -->\n",
              "<g id=\"edge2\" class=\"edge\">\n",
              "<title>var8646911284551352331&#45;&#45;var8646911284551352341</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M27,-143.83C27,-133 27,-119.29 27,-108.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352333 -->\n",
              "<g id=\"node3\" class=\"node\">\n",
              "<title>var8646911284551352333</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"171\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x13</text>\n",
              "</g>\n",
              "<!-- var8646911284551352332&#45;&#45;var8646911284551352333 -->\n",
              "<g id=\"edge3\" class=\"edge\">\n",
              "<title>var8646911284551352332&#45;&#45;var8646911284551352333</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M126.22,-162C131.95,-162 138.01,-162 143.74,-162\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352342 -->\n",
              "<g id=\"node6\" class=\"node\">\n",
              "<title>var8646911284551352342</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"99\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x22</text>\n",
              "</g>\n",
              "<!-- var8646911284551352332&#45;&#45;var8646911284551352342 -->\n",
              "<g id=\"edge4\" class=\"edge\">\n",
              "<title>var8646911284551352332&#45;&#45;var8646911284551352342</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M99,-143.83C99,-133 99,-119.29 99,-108.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352334 -->\n",
              "<g id=\"node4\" class=\"node\">\n",
              "<title>var8646911284551352334</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"243\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x14</text>\n",
              "</g>\n",
              "<!-- var8646911284551352333&#45;&#45;var8646911284551352334 -->\n",
              "<g id=\"edge5\" class=\"edge\">\n",
              "<title>var8646911284551352333&#45;&#45;var8646911284551352334</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M198.22,-162C203.95,-162 210.01,-162 215.74,-162\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352343 -->\n",
              "<g id=\"node7\" class=\"node\">\n",
              "<title>var8646911284551352343</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"171\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x23</text>\n",
              "</g>\n",
              "<!-- var8646911284551352333&#45;&#45;var8646911284551352343 -->\n",
              "<g id=\"edge6\" class=\"edge\">\n",
              "<title>var8646911284551352333&#45;&#45;var8646911284551352343</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M171,-143.83C171,-133 171,-119.29 171,-108.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352344 -->\n",
              "<g id=\"node8\" class=\"node\">\n",
              "<title>var8646911284551352344</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"243\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x24</text>\n",
              "</g>\n",
              "<!-- var8646911284551352334&#45;&#45;var8646911284551352344 -->\n",
              "<g id=\"edge7\" class=\"edge\">\n",
              "<title>var8646911284551352334&#45;&#45;var8646911284551352344</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M243,-143.83C243,-133 243,-119.29 243,-108.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352341&#45;&#45;var8646911284551352342 -->\n",
              "<g id=\"edge8\" class=\"edge\">\n",
              "<title>var8646911284551352341&#45;&#45;var8646911284551352342</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M54.22,-90C59.95,-90 66.01,-90 71.74,-90\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352351 -->\n",
              "<g id=\"node9\" class=\"node\">\n",
              "<title>var8646911284551352351</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"27\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x31</text>\n",
              "</g>\n",
              "<!-- var8646911284551352341&#45;&#45;var8646911284551352351 -->\n",
              "<g id=\"edge9\" class=\"edge\">\n",
              "<title>var8646911284551352341&#45;&#45;var8646911284551352351</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M27,-71.83C27,-61 27,-47.29 27,-36.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352342&#45;&#45;var8646911284551352343 -->\n",
              "<g id=\"edge10\" class=\"edge\">\n",
              "<title>var8646911284551352342&#45;&#45;var8646911284551352343</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M126.22,-90C131.95,-90 138.01,-90 143.74,-90\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352352 -->\n",
              "<g id=\"node10\" class=\"node\">\n",
              "<title>var8646911284551352352</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"99\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x32</text>\n",
              "</g>\n",
              "<!-- var8646911284551352342&#45;&#45;var8646911284551352352 -->\n",
              "<g id=\"edge11\" class=\"edge\">\n",
              "<title>var8646911284551352342&#45;&#45;var8646911284551352352</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M99,-71.83C99,-61 99,-47.29 99,-36.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352343&#45;&#45;var8646911284551352344 -->\n",
              "<g id=\"edge12\" class=\"edge\">\n",
              "<title>var8646911284551352343&#45;&#45;var8646911284551352344</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M198.22,-90C203.95,-90 210.01,-90 215.74,-90\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352353 -->\n",
              "<g id=\"node11\" class=\"node\">\n",
              "<title>var8646911284551352353</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"171\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x33</text>\n",
              "</g>\n",
              "<!-- var8646911284551352343&#45;&#45;var8646911284551352353 -->\n",
              "<g id=\"edge13\" class=\"edge\">\n",
              "<title>var8646911284551352343&#45;&#45;var8646911284551352353</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M171,-71.83C171,-61 171,-47.29 171,-36.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352354 -->\n",
              "<g id=\"node12\" class=\"node\">\n",
              "<title>var8646911284551352354</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"243\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x34</text>\n",
              "</g>\n",
              "<!-- var8646911284551352344&#45;&#45;var8646911284551352354 -->\n",
              "<g id=\"edge14\" class=\"edge\">\n",
              "<title>var8646911284551352344&#45;&#45;var8646911284551352354</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M243,-71.83C243,-61 243,-47.29 243,-36.41\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352351&#45;&#45;var8646911284551352352 -->\n",
              "<g id=\"edge15\" class=\"edge\">\n",
              "<title>var8646911284551352351&#45;&#45;var8646911284551352352</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M54.22,-18C59.95,-18 66.01,-18 71.74,-18\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352352&#45;&#45;var8646911284551352353 -->\n",
              "<g id=\"edge16\" class=\"edge\">\n",
              "<title>var8646911284551352352&#45;&#45;var8646911284551352353</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M126.22,-18C131.95,-18 138.01,-18 143.74,-18\"/>\n",
              "</g>\n",
              "<!-- var8646911284551352353&#45;&#45;var8646911284551352354 -->\n",
              "<g id=\"edge17\" class=\"edge\">\n",
              "<title>var8646911284551352353&#45;&#45;var8646911284551352354</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M198.22,-18C203.95,-18 210.01,-18 215.74,-18\"/>\n",
              "</g>\n",
              "</g>\n",
              "</svg>\n"
            ],
            "text/plain": [
              "<graphviz.sources.Source at 0x2d89d88e510>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "writer = gtsam.DotWriter(binaryEdges = True)\n",
        "writer.variablePositions = positions\n",
        "display(graphviz.Source(grid_graph.dot(writer=writer), engine='neato'))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_colamd_md"
      },
      "source": [
        "### COLAMD Ordering and Resulting Bayes Net\n",
        "\n",
        "Now, we compute the COLAMD ordering and eliminate the variables according to this order."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ordering_colamd_code",
        "outputId": "789abcde-f012-3456-789a-bcdef0123456"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "COLAMD Ordering: \n",
            "Position 0: x11, x31, x14, x34, x33, x24, x23, x32, x13, x22\n",
            "Position 10: x21, x12\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "COLAMD Bayes Net Structure:\n"
          ]
        },
        {
          "data": {
            "image/svg+xml": [
              "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
              "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
              " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
              "<!-- Generated by graphviz version 2.50.0 (0)\n",
              " -->\n",
              "<!-- Title: G Pages: 1 -->\n",
              "<svg width=\"393pt\" height=\"476pt\"\n",
              " viewBox=\"0.00 0.00 392.99 476.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
              "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 472)\">\n",
              "<title>G</title>\n",
              "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-472 388.99,-472 388.99,4 -4,4\"/>\n",
              "<!-- 0 -->\n",
              "<g id=\"node1\" class=\"node\">\n",
              "<title>0</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"142.39\" cy=\"-450\" rx=\"59.59\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"142.39\" y=\"-446.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x11, x21, x12</text>\n",
              "</g>\n",
              "<!-- 1 -->\n",
              "<g id=\"node2\" class=\"node\">\n",
              "<title>1</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"142.39\" cy=\"-378\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"142.39\" y=\"-374.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x13, x22 : x12, x21</text>\n",
              "</g>\n",
              "<!-- 0&#45;&gt;1 -->\n",
              "<g id=\"edge1\" class=\"edge\">\n",
              "<title>0&#45;&gt;1</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M142.39,-431.7C142.39,-423.98 142.39,-414.71 142.39,-406.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"145.89,-406.1 142.39,-396.1 138.89,-406.1 145.89,-406.1\"/>\n",
              "</g>\n",
              "<!-- 2 -->\n",
              "<g id=\"node3\" class=\"node\">\n",
              "<title>2</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"142.39\" cy=\"-306\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"142.39\" y=\"-302.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x32 : x13, x21, x22</text>\n",
              "</g>\n",
              "<!-- 1&#45;&gt;2 -->\n",
              "<g id=\"edge2\" class=\"edge\">\n",
              "<title>1&#45;&gt;2</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M142.39,-359.7C142.39,-351.98 142.39,-342.71 142.39,-334.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"145.89,-334.1 142.39,-324.1 138.89,-334.1 145.89,-334.1\"/>\n",
              "</g>\n",
              "<!-- 3 -->\n",
              "<g id=\"node4\" class=\"node\">\n",
              "<title>3</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"62.39\" cy=\"-234\" rx=\"62.29\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"62.39\" y=\"-230.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x31 : x21, x32</text>\n",
              "</g>\n",
              "<!-- 2&#45;&gt;3 -->\n",
              "<g id=\"edge3\" class=\"edge\">\n",
              "<title>2&#45;&gt;3</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M123.44,-288.41C113,-279.28 99.89,-267.81 88.5,-257.84\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"90.77,-255.18 80.94,-251.23 86.16,-260.45 90.77,-255.18\"/>\n",
              "</g>\n",
              "<!-- 4 -->\n",
              "<g id=\"node5\" class=\"node\">\n",
              "<title>4</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"223.39\" cy=\"-234\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"223.39\" y=\"-230.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x23 : x13, x22, x32</text>\n",
              "</g>\n",
              "<!-- 2&#45;&gt;4 -->\n",
              "<g id=\"edge4\" class=\"edge\">\n",
              "<title>2&#45;&gt;4</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M161.59,-288.41C172.01,-279.41 185.05,-268.14 196.46,-258.27\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"198.78,-260.9 204.06,-251.71 194.2,-255.6 198.78,-260.9\"/>\n",
              "</g>\n",
              "<!-- 5 -->\n",
              "<g id=\"node6\" class=\"node\">\n",
              "<title>5</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"223.39\" cy=\"-162\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"223.39\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x24 : x13, x23, x32</text>\n",
              "</g>\n",
              "<!-- 4&#45;&gt;5 -->\n",
              "<g id=\"edge5\" class=\"edge\">\n",
              "<title>4&#45;&gt;5</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M223.39,-215.7C223.39,-207.98 223.39,-198.71 223.39,-190.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"226.89,-190.1 223.39,-180.1 219.89,-190.1 226.89,-190.1\"/>\n",
              "</g>\n",
              "<!-- 6 -->\n",
              "<g id=\"node7\" class=\"node\">\n",
              "<title>6</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"143.39\" cy=\"-90\" rx=\"62.29\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"143.39\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x14 : x13, x24</text>\n",
              "</g>\n",
              "<!-- 5&#45;&gt;6 -->\n",
              "<g id=\"edge6\" class=\"edge\">\n",
              "<title>5&#45;&gt;6</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M204.44,-144.41C194,-135.28 180.89,-123.81 169.5,-113.84\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"171.77,-111.18 161.94,-107.23 167.16,-116.45 171.77,-111.18\"/>\n",
              "</g>\n",
              "<!-- 7 -->\n",
              "<g id=\"node8\" class=\"node\">\n",
              "<title>7</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"304.39\" cy=\"-90\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"304.39\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x33 : x23, x24, x32</text>\n",
              "</g>\n",
              "<!-- 5&#45;&gt;7 -->\n",
              "<g id=\"edge7\" class=\"edge\">\n",
              "<title>5&#45;&gt;7</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M242.59,-144.41C253.01,-135.41 266.05,-124.14 277.46,-114.27\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"279.78,-116.9 285.06,-107.71 275.2,-111.6 279.78,-116.9\"/>\n",
              "</g>\n",
              "<!-- 8 -->\n",
              "<g id=\"node9\" class=\"node\">\n",
              "<title>8</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"304.39\" cy=\"-18\" rx=\"62.29\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"304.39\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x34 : x24, x33</text>\n",
              "</g>\n",
              "<!-- 7&#45;&gt;8 -->\n",
              "<g id=\"edge8\" class=\"edge\">\n",
              "<title>7&#45;&gt;8</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M304.39,-71.7C304.39,-63.98 304.39,-54.71 304.39,-46.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"307.89,-46.1 304.39,-36.1 300.89,-46.1 307.89,-46.1\"/>\n",
              "</g>\n",
              "</g>\n",
              "</svg>\n"
            ],
            "text/plain": [
              "<graphviz.sources.Source at 0x2d89d882990>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# COLAMD (Column Approximate Minimum Degree) ordering\n",
        "colamd_ordering = Ordering.ColamdSymbolicFactorGraph(grid_graph)\n",
        "print(\"COLAMD Ordering: \")\n",
        "colamd_ordering.print()\n",
        "\n",
        "# Eliminate using COLAMD ordering\n",
        "bayes_tree_colamd = grid_graph.eliminateMultifrontal(colamd_ordering)\n",
        "\n",
        "# Visualize the resulting Bayes Net\n",
        "print(\"\\nCOLAMD Bayes Net Structure:\")\n",
        "display(graphviz.Source(bayes_tree_colamd.dot()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_metis_md"
      },
      "source": [
        "### METIS Ordering and Resulting Bayes Net\n",
        "\n",
        "Next, we compute the METIS ordering and visualize its resulting Bayes Net. Compare its structure to the one generated by COLAMD."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "metadata": {
        "id": "ordering_metis_code"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "METIS Ordering: \n",
            "Position 0: x34, x23, x14, x24, x13, x32, x11, x31, x21, x12\n",
            "Position 10: x33, x22\n",
            "\n",
            "METIS Bayes Net Structure:\n"
          ]
        },
        {
          "data": {
            "image/svg+xml": [
              "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
              "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
              " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
              "<!-- Generated by graphviz version 2.50.0 (0)\n",
              " -->\n",
              "<!-- Title: G Pages: 1 -->\n",
              "<svg width=\"557pt\" height=\"260pt\"\n",
              " viewBox=\"0.00 0.00 556.79 260.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
              "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 256)\">\n",
              "<title>G</title>\n",
              "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-256 552.79,-256 552.79,4 -4,4\"/>\n",
              "<!-- 8 -->\n",
              "<g id=\"node1\" class=\"node\">\n",
              "<title>8</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"223.39\" cy=\"-234\" rx=\"77.99\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"223.39\" y=\"-230.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x21, x12, x33, x22</text>\n",
              "</g>\n",
              "<!-- 9 -->\n",
              "<g id=\"node2\" class=\"node\">\n",
              "<title>9</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"62.39\" cy=\"-162\" rx=\"62.29\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"62.39\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x11 : x12, x21</text>\n",
              "</g>\n",
              "<!-- 8&#45;&gt;9 -->\n",
              "<g id=\"edge1\" class=\"edge\">\n",
              "<title>8&#45;&gt;9</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M188.46,-217.81C163.83,-207.1 130.72,-192.71 104.61,-181.36\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"105.9,-178.1 95.34,-177.32 103.11,-184.52 105.9,-178.1\"/>\n",
              "</g>\n",
              "<!-- 10 -->\n",
              "<g id=\"node3\" class=\"node\">\n",
              "<title>10</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"223.39\" cy=\"-162\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"223.39\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x31 : x21, x22, x33</text>\n",
              "</g>\n",
              "<!-- 8&#45;&gt;10 -->\n",
              "<g id=\"edge2\" class=\"edge\">\n",
              "<title>8&#45;&gt;10</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M223.39,-215.7C223.39,-207.98 223.39,-198.71 223.39,-190.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"226.89,-190.1 223.39,-180.1 219.89,-190.1 226.89,-190.1\"/>\n",
              "</g>\n",
              "<!-- 12 -->\n",
              "<g id=\"node5\" class=\"node\">\n",
              "<title>12</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"411.39\" cy=\"-162\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"411.39\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x13 : x12, x22, x33</text>\n",
              "</g>\n",
              "<!-- 8&#45;&gt;12 -->\n",
              "<g id=\"edge4\" class=\"edge\">\n",
              "<title>8&#45;&gt;12</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M262.81,-218.33C291.66,-207.58 331.02,-192.93 361.98,-181.4\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"363.56,-184.54 371.71,-177.78 361.12,-177.98 363.56,-184.54\"/>\n",
              "</g>\n",
              "<!-- 11 -->\n",
              "<g id=\"node4\" class=\"node\">\n",
              "<title>11</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"218.39\" cy=\"-90\" rx=\"80.69\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"218.39\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x32 : x22, x31, x33</text>\n",
              "</g>\n",
              "<!-- 10&#45;&gt;11 -->\n",
              "<g id=\"edge3\" class=\"edge\">\n",
              "<title>10&#45;&gt;11</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M222.16,-143.7C221.61,-135.98 220.95,-126.71 220.33,-118.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"223.82,-117.83 219.62,-108.1 216.84,-118.33 223.82,-117.83\"/>\n",
              "</g>\n",
              "<!-- 13 -->\n",
              "<g id=\"node6\" class=\"node\">\n",
              "<title>13</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"415.39\" cy=\"-90\" rx=\"98.58\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"415.39\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x23, x24 : x13, x22, x33</text>\n",
              "</g>\n",
              "<!-- 12&#45;&gt;13 -->\n",
              "<g id=\"edge5\" class=\"edge\">\n",
              "<title>12&#45;&gt;13</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M412.38,-143.7C412.82,-135.98 413.35,-126.71 413.85,-118.11\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"417.34,-118.29 414.42,-108.1 410.35,-117.89 417.34,-118.29\"/>\n",
              "</g>\n",
              "<!-- 14 -->\n",
              "<g id=\"node7\" class=\"node\">\n",
              "<title>14</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"343.39\" cy=\"-18\" rx=\"62.29\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"343.39\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x14 : x13, x24</text>\n",
              "</g>\n",
              "<!-- 13&#45;&gt;14 -->\n",
              "<g id=\"edge6\" class=\"edge\">\n",
              "<title>13&#45;&gt;14</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M397.97,-72.05C388.94,-63.28 377.78,-52.43 367.91,-42.83\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"370.27,-40.25 360.66,-35.79 365.39,-45.27 370.27,-40.25\"/>\n",
              "</g>\n",
              "<!-- 15 -->\n",
              "<g id=\"node8\" class=\"node\">\n",
              "<title>15</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"486.39\" cy=\"-18\" rx=\"62.29\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"486.39\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x34 : x24, x33</text>\n",
              "</g>\n",
              "<!-- 13&#45;&gt;15 -->\n",
              "<g id=\"edge7\" class=\"edge\">\n",
              "<title>13&#45;&gt;15</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M432.58,-72.05C441.48,-63.28 452.49,-52.43 462.22,-42.83\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"464.7,-45.3 469.37,-35.79 459.79,-40.32 464.7,-45.3\"/>\n",
              "</g>\n",
              "</g>\n",
              "</svg>\n"
            ],
            "text/plain": [
              "<graphviz.sources.Source at 0x2d89d882990>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "metis_ordering = Ordering.MetisSymbolicFactorGraph(grid_graph)\n",
        "print(\"METIS Ordering: \")\n",
        "metis_ordering.print()\n",
        "\n",
        "# Eliminate using METIS ordering\n",
        "bayes_tree_metis = grid_graph.eliminateMultifrontal(metis_ordering)\n",
        "\n",
        "# Visualize the resulting Bayes Net\n",
        "print(\"\\nMETIS Bayes Net Structure:\")\n",
        "display(graphviz.Source(bayes_tree_metis.dot()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_comparison_md"
      },
      "source": [
        "### Comparison\n",
        "\n",
        "Observe the differences in the Bayes tree structures produced by COLAMD and METIS:\n",
        "\n",
        "*   **COLAMD:** Often produces a more 'stringy' or deeper Bayes tree. The cliques (conditionals in the Bayes tree) might be smaller initially but can grow larger towards the root (variables eliminated last).\n",
        "*   **METIS:** Tends to produce a more 'bushy' or balanced tree. It tries to partition the graph, eliminating variables within partitions first, leading to potentially larger initial cliques but often a shallower overall structure and smaller separators (variables connecting cliques high up in the tree). \n",
        "\n",
        "When should you choose one over the other? The best choice often depends on the specific problem structure and computational goals:\n",
        "\n",
        "*   **Use COLAMD when:**\n",
        "    *   You need a good, general-purpose ordering quickly. COLAMD is typically much faster *to compute the ordering itself* than METIS.\n",
        "    *   You are primarily using a *sequential* solver (running on a single CPU core). COLAMD's greedy strategy is often well-suited for minimizing fill-in in this scenario.\n",
        "    *   The factor graph is relatively small or doesn't have a highly regular structure where complex partitioning would yield significant benefits.\n",
        "\n",
        "*   **Use METIS when:**\n",
        "    *   You are aiming for maximum performance with a *parallel* solver (e.g., using GTSAM's multifrontal solvers with TBB). METIS's graph partitioning approach tends to create more balanced Bayes trees, which allows for better workload distribution across multiple CPU cores.\n",
        "    *   You are dealing with very large-scale problems, especially those with a regular structure (like large grids, meshes from finite element analysis, or extensive SLAM maps). On such problems, METIS can sometimes find an ordering with significantly less *total fill-in* than COLAMD, leading to faster factorization, even if computing the ordering itself takes longer.\n",
        "    *   The cost of computing the ordering is negligible compared to the cost of the subsequent factorization and solve steps (e.g., you compute the ordering once for a structure that is solved repeatedly).\n",
        "\n",
        "**In summary:** COLAMD is a robust and fast default. METIS is often the preferred choice for large-scale problems and parallel execution, potentially offering better final factorization performance at the cost of a slower ordering computation. Experimentation on your specific problem type might be necessary to determine the optimal choice.\n",
        "\n",
        "For more information on COLAMD and METIS, see [Factor Graphs for Robot Perception](https://www.cs.cmu.edu/~kaess/pub/Dellaert17fnt.pdf)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_constrained_md"
      },
      "source": [
        "### Constrained Ordering\n",
        "\n",
        "Sometimes, we want to force certain variables to be eliminated last (e.g., the current robot pose in SLAM). `Ordering.ColamdConstrainedLast` allows this for COLAMD."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ordering_constrained_code",
        "outputId": "89abcdef-0123-4567-89ab-cdef01234567"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Constrained COLAMD (x11, x34 last):\n",
            "Position 0: x31, x32, x21, x14, x24, x13, x23, x33, x22, x12\n",
            "Position 10: x34, x11\n"
          ]
        }
      ],
      "source": [
        "# Example: Constrained COLAMD forcing corners (x0, x24) to be eliminated last\n",
        "# Note: We use the grid_graph defined earlier\n",
        "corner_keys = gtsam.KeyVector([X_grid(0, 0), X_grid(ROWS-1, COLS-1)])\n",
        "constrained_ordering = Ordering.ColamdConstrainedLastSymbolicFactorGraph(grid_graph, corner_keys)\n",
        "print(f\"Constrained COLAMD ({gtsam.DefaultKeyFormatter(corner_keys[0])}, {gtsam.DefaultKeyFormatter(corner_keys[1])} last):\")\n",
        "constrained_ordering.print()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_access_md"
      },
      "source": [
        "## Accessing Elements\n",
        "\n",
        "An `Ordering` behaves like a vector of keys."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ordering_access_code",
        "outputId": "89abcdef-0123-4567-89ab-cdef01234567"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "COLAMD Ordering size: 12\n",
            "Key at position 0 (COLAMD): x11\n"
          ]
        }
      ],
      "source": [
        "print(f\"COLAMD Ordering size: {colamd_ordering.size()}\")\n",
        "\n",
        "# Access by index\n",
        "key_at_0 = colamd_ordering.at(0)\n",
        "print(f\"Key at position 0 (COLAMD): {gtsam.DefaultKeyFormatter(key_at_0)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ordering_append_md"
      },
      "source": [
        "## Appending Keys\n",
        "\n",
        "You can append keys to an existing ordering using `push_back`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ordering_append_code",
        "outputId": "9abcdef0-1234-5678-9abc-def012345678"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appended Ordering: Position 0: x11, x31, x14, x34, x33, x24, x23, x32, x13, x22\n",
            "Position 10: x21, x12, l0, x12\n"
          ]
        }
      ],
      "source": [
        "# Use the COLAMD ordering from the grid example\n",
        "appended_ordering = Ordering(colamd_ordering)\n",
        "appended_ordering.push_back(L(0)) # Append a landmark key\n",
        "appended_ordering.push_back(X(ROWS*COLS)) # Append a new pose key x25\n",
        "\n",
        "appended_ordering.print(\"Appended Ordering: \")"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "py312",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
